﻿using Devil;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

namespace GameToolkit
{
    [AddComponentMenu("Navigation/Runtime NavMeshRoadModifier", 33)]
    public class NavMeshRoadModifier : MonoBehaviour, INavMeshSource
    {
        public enum EModifyType
        {
            ModifyVolume,
            BoxVolume,
        }

#pragma warning disable CS0649
        [SerializeField]
        EModifyType m_ModifyType;

        [NavAreaType]
        [SerializeField]
        int m_AreaType;

        [HideInInspector]
        [NavAgentType]
        [SerializeField]
        List<int> m_AffectAgents = new List<int>();

        [SerializeField]
        Vector3 m_VolumeSize = new Vector3(1, 1f, 0.5f);

        [SerializeField, HideInInspector]
        public Vector3[] m_Points;

        [SerializeField, HideInInspector]
        bool m_IsRuntime;

        [SerializeField, HideInInspector]
        public Bounds m_LocalBounds;
        public Bounds localBounds { get { return m_LocalBounds; } }

        public Matrix4x4 localToWorldMatrix => transform.localToWorldMatrix;

        public Matrix4x4 worldToLocalMatrix => transform.worldToLocalMatrix;

#pragma warning restore

        public bool AffectOnAgent(int agentId)
        {
            if (m_AffectAgents.Count == 1 && m_AffectAgents[0] == -1)
                return true;
            else
                return m_AffectAgents.Contains(agentId);
        }

        private void OnEnable()
        {
            if (m_IsRuntime)
            {
                RuntimeNavMeshBuilder.AddBuildSource(this);
            }
        }

        private void OnDisable()
        {
            if (m_IsRuntime)
                RuntimeNavMeshBuilder.RemoveBuildSource(this);
        }

        public void GetBuildSource(int defaultArea, ICollection<NavMeshBuildSource> sources)
        {
            if (m_Points != null)
            {
                var area = m_AreaType == 0 ? defaultArea : m_AreaType;
                if (m_ModifyType == EModifyType.BoxVolume)
                {
                    for (int i = 1; i < m_Points.Length; i++)
                    {
                        sources.Add(GetBuildSource(area, m_Points[i], m_Points[i - 1]));
                    }
                }
                else
                {
                    for (int i = 1; i < m_Points.Length; i++)
                    {
                        sources.Add(GetModifySource(area, m_Points[i], m_Points[i - 1]));
                    }
                }
            }
        }

        public NavMeshBuildSource GetModifySource(int areaType, Vector3 from, Vector3 to)
        {
            var mcenter = transform.localToWorldMatrix.MultiplyPoint((from + to) * 0.5f);
            var scale = transform.lossyScale;
            var msize = new Vector3(m_VolumeSize.x * Mathf.Abs(scale.x),
                m_VolumeSize.y * Mathf.Abs(scale.y),
                (m_VolumeSize.z + (from - to).magnitude) * Mathf.Abs(scale.z));

            var src = new NavMeshBuildSource();
            src.shape = NavMeshBuildSourceShape.ModifierBox;
            src.transform = Matrix4x4.TRS(mcenter, transform.rotation * Quaternion.LookRotation(from - to), Vector3.one);
            src.size = msize;
            src.area = areaType;
            return src;
        }

        NavMeshBuildSource GetBuildSource(int areaType, Vector3 from, Vector3 to)
        {
            from = transform.localToWorldMatrix.MultiplyPoint(from);
            to = transform.localToWorldMatrix.MultiplyPoint(to);
            var up = Vector3.Cross(to - from, Vector3.right);
            if (up.y < 0)
                up = -up;
            up = up.normalized;
            var mcenter = (from + to) * 0.5f;
            var scale = transform.lossyScale;
            var msize = new Vector3(m_VolumeSize.x * Mathf.Abs(scale.x),
                m_VolumeSize.y * Mathf.Abs(scale.y) * 0.5f,
                (m_VolumeSize.z + (from - to).magnitude) * Mathf.Abs(scale.z));
            mcenter -= up * msize.y * 0.5f;

            var src = new NavMeshBuildSource();
            src.shape = NavMeshBuildSourceShape.Box;
            src.transform = Matrix4x4.TRS(mcenter, transform.rotation * Quaternion.LookRotation(from - to), Vector3.one);
            src.size = msize;
            src.area = areaType;
            return src;
        }

#if UNITY_EDITOR
        public static NavMeshRoadModifier selection;
        private void OnDrawGizmos()
        {
            if (m_Points == null || selection == null)
                return;
            var mat = transform.localToWorldMatrix;
            Gizmos.color = selection == this ? Color.cyan : Color.gray;
            var size = m_VolumeSize;
            for (int i = 1; i < m_Points.Length; i++)
            {
                var p0 = m_Points[i];
                var p1 = m_Points[i - 1];
                //Gizmos.matrix = mat;
                //Gizmos.DrawLine(n.point, p);
                Gizmos.matrix = mat * Matrix4x4.TRS((p0 + p1) * 0.5f, Quaternion.LookRotation(p0 - p1), Vector3.one);
                size.z = (p0 - p1).magnitude + m_VolumeSize.z;
                Gizmos.DrawWireCube(Vector3.zero, size);
            }
            if (selection == this && MathExt.IsDifferent(m_LocalBounds.size, Vector3.zero, 1))
            {
                Gizmos.color = Color.gray;
                Gizmos.matrix = mat;
                Gizmos.DrawWireCube(m_LocalBounds.center, m_LocalBounds.size);
            }
        }

#endif
    }

}